Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/dependent feature toggles #140

Merged
merged 13 commits into from
Sep 27, 2023
Merged

Conversation

FredrikOseberg
Copy link
Contributor

Implements dependent feature toggles for the golang-sdk

client.go Outdated
if parent.Variants != nil && len(*parent.Variants) > 0 {
variantName := uc.getVariantWithoutMetrics(parent.Feature, WithVariantContext(context)).Name
if contains(*parent.Variants, variantName) {
continue
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure how idiomatic continue is in golang but I'd rather negate the contains and early exit with return false

@@ -345,6 +340,48 @@ func (uc *Client) isEnabled(feature string, options ...FeatureOption) api.Strate
}
}

func (uc *Client) isParentDependencySatisfied(feature *api.Feature, context context.Context) bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

func (uc *Client) isParentDependencySatisfied(feature *api.Feature, context context.Context) bool {
    if feature == nil || feature.Dependencies == nil || len(*feature.Dependencies) == 0 {
        return true
    }

    for _, parent := range *feature.Dependencies {
        parentToggle := uc.repository.getToggle(parent.Feature)
        isEnabled := uc.isEnabled(parent.Feature, WithContext(context)).Enabled

        if parentToggle == nil || 
           (parentToggle.Dependencies != nil && len(*parentToggle.Dependencies) > 0) || 
           (parent.Enabled != nil && !isEnabled) {
            return false
        }

        if parent.Enabled == nil {
            variantName := uc.getVariantWithoutMetrics(parent.Feature, WithVariantContext(context)).Name
            hasValidVariant := parent.Variants != nil && len(*parent.Variants) > 0 && contains(*parent.Variants, variantName)
            
            if !hasValidVariant && !isEnabled {
                return false
            }
        }
    }

    return true
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless we write an every helper, we can't return out here or we will exit the entire function without properly looping over all the dependencies.

@FredrikOseberg FredrikOseberg marked this pull request as ready for review September 27, 2023 09:10
Copy link
Contributor

@kwasniew kwasniew left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

api/feature.go Outdated
Dependencies *[]FeatureDependencies `json:"dependencies"`
}

type FeatureDependencies struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I called this just Dependency in Node SDK with an option to other dependencies. The property inside Feature is indicating what type of dependency it is. If we don't expose this type in the public API then I'm ok with any name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dependency works fine

client.go Outdated
}

if f.Dependencies != nil && len(*f.Dependencies) > 0 {
parentEnabled := uc.isParentDependencySatisfied(f, *ctx)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't it more than parent enabled? the variable name may be misleading

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree

return handleFallback(opts, feature, ctx)
}

if f.Dependencies != nil && len(*f.Dependencies) > 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious why you decided to do this check outside the isParentDependencySatisfied. I made the opposite decision but didn't have a strong opinion here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me it made more sense readability wise. If I don't have dependencies or the length of dependencies is 0 then I don't need to do check if isParentDependencySatisfied(f, *ctx). Moving it inside that function obfuscates the check that determines whether or not that function should actually be run. I don't have strong opinions about this either, but I found it easier to reason about this way because now the function is limited to doing one thing and one thing only.

@FredrikOseberg FredrikOseberg merged commit 2b58cc4 into v3 Sep 27, 2023
5 checks passed
@FredrikOseberg FredrikOseberg deleted the feat/dependent-feature-toggles branch September 27, 2023 14:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

2 participants